package com.ieng.kican.modules.security.group;

import com.ieng.kican.common.utils.IDUtils;
import com.ieng.kican.common.utils.IUtil;
import com.ieng.kican.modules.security.config.properties.JwtAuthProperties;
import com.ieng.kican.modules.cache.RedisCache;
import com.ieng.kican.modules.security.domain.JwtAuthUser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class TokenProvider implements InitializingBean {
    private final static String AUTH_KEY = "auth_baz";
    private final JwtAuthProperties properties;
    private final RedisCache redisCache;
    private JwtParser jwtParser;
    private JwtBuilder jwtBuilder;

    public TokenProvider(JwtAuthProperties properties, RedisCache redisCache) {
        this.properties = properties;
        this.redisCache = redisCache;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        byte[] bytes = Decoders.BASE64.decode(properties.getSecret());
        Key key = Keys.hmacShaKeyFor(bytes);
        jwtParser = Jwts.parserBuilder().setSigningKey(key).build();
        jwtBuilder = Jwts.builder().signWith(key, SignatureAlgorithm.HS256);
    }

    public String createToken(Authentication authentication) {
        String authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(","));

        return jwtBuilder.setId(IDUtils.simpleUUID())
                .claim(AUTH_KEY, authorities)
                .setSubject(authentication.getName())
                .compact();
    }

    public Authentication getAuthentication(String token){
        Claims claims = getClaims(token);
        Collection<? extends GrantedAuthority> authorities = Arrays.stream(claims.get(AUTH_KEY).toString().split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());

        JwtAuthUser authUser = new JwtAuthUser(claims.getSubject(), "", authorities);
        return new UsernamePasswordAuthenticationToken(authUser, token, authorities);
    }

    public void refresh(String token) {
        Claims claims = getClaims(token);
        String uuid = claims.getId();
        long time = redisCache.getExpire(properties.getSessionName() + uuid);
        long differ = time - System.currentTimeMillis();
        if (IUtil.before(differ, properties.getFreshTime().toMillis())) {
            long renewal = time + properties.getRenewal().toMillis();
            redisCache.expire(properties.getSessionName() + uuid, renewal, TimeUnit.MILLISECONDS);
        }
    }

    public String getToken(HttpServletRequest request) {
        final String header = request.getHeader(properties.getHeader());
        if (header != null && header.startsWith(properties.getPrefix())) {
            return header.substring(properties.getPrefix().length() + 1);
        }
        return null;
    }

    public Claims getClaims(String token) {
        return jwtParser.parseClaimsJws(token).getBody();
    }
}
